package com.neusoft.hifly.dynamicds.multi.aop;

import java.lang.reflect.Method;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.neusoft.hifly.commons.database.DBUtil;
import com.neusoft.hifly.core.log.Log;
import com.neusoft.hifly.core.utils.OrganCodeContextHolder;
import com.neusoft.hifly.dynamicds.druid.DruidDBConfig;
import com.neusoft.hifly.dynamicds.multi.MultiDataSourceContextHolder;
import com.neusoft.hifly.dynamicds.multi.MultiOrganDataSource;
import com.neusoft.hifly.dynamicds.multi.annotation.MultiDynamicRoutingDataSource;
import com.neusoft.hifly.dynamicds.multi.datasource.dao.IDatasourceRouterDao;
import com.neusoft.hifly.dynamicds.multi.datasource.domain.DatasourceRouterObj;

/**
 * Title: 分布式动态数据源路由
 * <p>
 * Description: 动态数据源切换AOP
 * <p>
 * 这里使用@Before和@After，在调用目标方法前，进行aop拦截，通过解析注解上的值来切换数据源。在调用方法结束后，切回默认数据源。如果目标方法无返回值，也可以使用@Around，调用ProceedingJoinPoint的proceed()方法前切换数据源，调用proceed()方法后切回默认数据源。
 * <p>
 * Copyright: 2019
 * <p>
 * Company: 东软汉枫
 *
 * @author lh_neu
 * @version 6.2.6
 */
@Aspect
@Component
public class MultiDataSourceAop {

	@Autowired(required = false)
	private DruidDBConfig druidDBConfig;

	@Autowired(required = false)
	private IDatasourceRouterDao datasourceRouterDao;

	/** * @within匹配类上的注解 * @annotation匹配方法上的注解 */
	@Pointcut("@within(com.neusoft.hifly.dynamicds.multi.annotation.MultiDynamicRoutingDataSource) || @annotation(com.neusoft.hifly.dynamicds.multi.annotation.MultiDynamicRoutingDataSource)")
	public void pointCut() {
	}

	@Before(value = "pointCut()")
	public void beforeOpt(final JoinPoint joinPoint) {

		final Object target = joinPoint.getTarget();
		final Class<?> clazz = target.getClass();
		final Method[] methods = clazz.getMethods();
		MultiDynamicRoutingDataSource annotation = null;
		for (final Method method : methods) {
			if (joinPoint.getSignature().getName().equals(method.getName())) {
				annotation = method.getAnnotation(MultiDynamicRoutingDataSource.class);
				if (annotation == null) {
					annotation = joinPoint.getTarget().getClass().getAnnotation(MultiDynamicRoutingDataSource.class);
					if (annotation != null) {
						break;
					}
				}
			}
		}

		// 通过该租户编码切换数据源
		final String organCode = OrganCodeContextHolder.get();
		if (StringUtils.isEmpty(organCode)) {
			return;
		}
		final DatasourceRouterObj router = datasourceRouterDao.getByOrganCode(organCode);
		if (router != null) {

			// 去数据库查询数据源配置信息
			DataSource ds = druidDBConfig
					.createDataSource(DBUtil.getInstanse().connectionDriver(router.getDbType()),
							DBUtil.getInstanse().connectionUrl(router.getDbType(), router.getDbUrl(),
									router.getDbPort(), router.getDbName(), router.getDbCharacter()),
							router.getDbUser(), router.getDbPassword());

			MultiOrganDataSource organDataSource = new MultiOrganDataSource();
			organDataSource.setOrganCode(organCode);
			organDataSource.setDataSource(ds);

			MultiDataSourceContextHolder.setOrganDataSource(organDataSource);
		} else {
			MultiDataSourceContextHolder.clearOrganDataSource();
		}
	}

	@After(value = "pointCut()")
	public void afterOpt() {
		Log.debug("切回默认数据库");
		MultiDataSourceContextHolder.clearOrganDataSource();
	}
}
